/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     
 * Description  :     
 * Change log   :     
 * To Do        :
 ************************************************************************/
using System;
using System.Threading;
using SMLib.Automata.MM;
using System.Net.Sockets;
using System.Net;
using SMLib.Automata.Base;

namespace SMLib
{
	public class SUL_MM_WrapperTcp
	{
		TcpListener server;
		IBlackBox_MM machine;
		
		public SUL_MM_WrapperTcp (int port, IBlackBox_MM machine)
		{      
			IPAddress localAddr = IPAddress.Parse ("127.0.0.1");
			server = new TcpListener (localAddr, port);
			this.machine=machine;
		}
		
		public void Start ()
		{
			Thread t=new Thread(new ThreadStart(ListenThread));
			t.Start();
		}
		
		private int DoBlockingread(NetworkStream stream,byte[] buff,int offset,int size,int timeout)
		{
			DateTime startTime=DateTime.Now;
			int totalCount=0;
			while(true)
			{
				int count=stream.Read(buff, offset+totalCount, size-totalCount);
				totalCount+=count;
				if(totalCount==size)
					return totalCount;
				
				DateTime currentTime=DateTime.Now;
				TimeSpan sp=currentTime-startTime;
				if(sp.Milliseconds>timeout)
					return totalCount;
				else
					Thread.Sleep(10);				
			}			
		}
		
		void ListenThread()
		{			
			server.Start();
			TcpClient client = server.AcceptTcpClient();            
	        Console.WriteLine("Connected!");
    	    NetworkStream inputStream = client.GetStream();
			byte[] buff=new byte[1000];
			bool endRecieved=false;
			while(!endRecieved)
			{
				//SMLib.TcpHelper.PACKET p = new TcpHelper.PACKET ();
				Console.Write("read packet...");
				DoBlockingread(inputStream,buff, 0, 4,3000);
				Console.WriteLine("Recieved something!");
				int size=(buff[2]*256+buff[3]);
				
				if(size!=0)
					DoBlockingread(inputStream,buff, 4, size,3000);
				Console.WriteLine("{"+buff[0]+" "+buff[1]+" "+buff[2]+" "+buff[3]+"}");
				
				SMLib.TcpHelper.PACKET r=SMLib.TcpHelper.PACKET.FromBytes(buff,size+4);
				switch(r.TYPE)
				{
				//* reset
				case SMLib.TcpHelper.PACKET.TYPE_RESET:
					Console.WriteLine("Reset");
					machine.Reset();
					byte[] answer= {0,4,0,0};
					inputStream.Write(answer,0,4);
					break;
				//* input					
				case SMLib.TcpHelper.PACKET.TYPE_INPUT:
					Console.WriteLine("Input: "+r.CONTENT);
					ISymbol response=machine.Step(new StringSymbol(r.CONTENT));
					SMLib.TcpHelper.PACKET r2=new SMLib.TcpHelper.PACKET();
					if(response==null)
					{
						r2.TYPE=SMLib.TcpHelper.PACKET.TYPE_NOK;
					}
					else
					{
						r2.TYPE=TcpHelper.PACKET.TYPE_OUTPUT;
						r2.CONTENT=response.ToString();
					}
					byte[] data=r2.ToBytes();
					inputStream.Write(data,0,data.Length);
					break;
				//* LIST_INPUT_REQ
				case SMLib.TcpHelper.PACKET.TYPE_LIST_INPUT_REQ:
					Console.WriteLine("Sigma?");
					string alphabet="";
					foreach(ISymbol s in machine.InputAlphabet.AllSymbols())
					{
						alphabet+=s.ToString()+";";
					}
					SMLib.TcpHelper.PACKET r3=new SMLib.TcpHelper.PACKET();
					r3.TYPE=TcpHelper.PACKET.TYPE_LIST_OF_INPUTS;
					r3.CONTENT=alphabet;
					byte[] data2=r3.ToBytes();
					inputStream.Write(data2,0,data2.Length);
					break;
				case SMLib.TcpHelper.PACKET.TYPE_END:
					endRecieved=true;
					Console.WriteLine("Close packet recieved. Closing...");
					break;
				default:
					Console.WriteLine("Unknown Packet!"+r.TYPE);
					break;
				}				
			}
			inputStream.Close();
			server.Stop();
			Console.WriteLine("Done!");
		}
	}
}

